import static org.junit.Assert.*;
import junit.framework.TestCase;

import org.junit.Before;
import org.junit.Test;


public class VendingMachineTest extends TestCase{
	String itemName1, itemName2, itemName3, itemName4;
	double price1, price2, price3, price4;
	VendingMachineItem vmi1, vmi2, vmi3, vmi4;
	boolean b;
	
	VendingMachine vm = new VendingMachine();
	
	
	// setting up the test environment, i.e., creating a test object
	@Before
	public void setUp() throws Exception {
		itemName1 = "Pepsi";
		price1 = 1.50;
		itemName2 = "CokeCole";
		price2 = 1.75;
		itemName3 = "MtnDew";
		price3 = 1.25;
		itemName4 = "SpringWater";
		price4 = 1.00;
		vmi1 = new VendingMachineItem(itemName1, price1);
		vmi2 = new VendingMachineItem(itemName2, price2);
		vmi3 = new VendingMachineItem(itemName3, price3);
		vmi4 = new VendingMachineItem(itemName4, price4);
	}
	
	@Test
	public void testAddItem() {
		// testing the addItem method that it adds the item to the vending machine at the slot
		// by not throwing an exception
		b = true;
		try {
			vm.addItem(vmi1, "A");
			vm.addItem(vmi2, "B");
			vm.addItem(vmi3, "C");
			vm.addItem(vmi4, "D");
		} catch (Exception e) {
			b = false;
		}
		assertTrue(b);
		
		//testing for the valid exception is thrown when trying to assign an item at occupied slot.
		b = true;
		try {
			vm.addItem(vmi4, "A");
		} catch (Exception e) {
			assertEquals("Slot "+"A"+" already occupied",e.getMessage());
			b = false;
		}
		
		// Check to see that the catch block got executed in the above try-catch block
		// without this the test is not complete as the catch block may be run and test might pass
		assertFalse(b);
		
		// test case for validating the exception thrown for an invalid code while adding a item to vending machine
		b = true;
		try {
			vm.addItem(vmi4, "E");
		} catch (Exception e) {
			assertEquals("Invalid code for vending machine item",e.getMessage());
			b = false;
		}
		
		// Check to see that the catch block got executed in the above try-catch block
		assertFalse(b);
		
		// test case to verify that getItem method returns the valid Item for the given slot
		assertEquals(vmi1,vm.getItem("A"));
		assertNotSame(vmi2,vm.getItem("A"));
		}

	@Test
	public void testRemoveItem() {
		// test to see that removeItem() retruns and removes the item from the given slot
		b = true;
		
		// add the item to remove it, addItem() is already test earlier, so this is 
		// a valid test case
		vm.addItem(vmi1, "A"); 

		// check to see the getItem() doesn't return null, there is item at the slot 
		assertNotNull(vm.getItem("A"));
		
		try {
			assertEquals(vmi1,vm.removeItem("A"));
		} catch (Exception e) {
			b = false;
		}
		
		// test to see that the exception is not thrown, catch block not executed
		assertTrue(b);
		
		// test to see that there is no item at the given slot after removeItem()
		// the getItem() method is tested earlier hence this is a valid test case
		assertNull(vm.getItem("A"));
		
		// setting the boolean variable b to true for next test case
		// this test case to check the valid exception message is thrown for
		// trying ot remove an item from an empty slot
		b = true;
		try {
			vm.removeItem("A");
		} catch (Exception e) {
			// asserting the exception messages are as expected
			assertEquals("Slot "+"A"+" is empty -- cannot remove item",e.getMessage());
			b = false;
		}
		// check to see that an exception has thrown in the above code and catch block got executed
		assertFalse(b);
		
	}

	@Test
	public void testInsertMoney() {
		
		// test case to trying to insert a negative money
		b = true;
		try {
			vm.insertMoney(-20.00);
		} catch (Exception e) {
			assertEquals("Invalid amount.  Amount must be >= 0", e.getMessage());
			b = false;
		}
		assertFalse(b);

		// test case to insert a zero money, according to the program this is a valid scenario
		// but this is not in real case, so this might be a bug money inserted should always be greater than zero
		b = true;
		try {
			vm.insertMoney(00.00);
		} catch (Exception e) {
			b = false;
		}
		// test to see that the catch block was not exceuted in the above try-catch, hence no exception as expected
		assertTrue(b);
		
		// testing th system with valid money , positive testing
		b = true;
		try {
			vm.insertMoney(20.00);
		} catch (Exception e) {
			b = false;
		}
		// test to see that the catch block was not exceuted in the above try-catch, hence no exception as expected
		assertTrue(b);
				
	}

	@Test
	public void testGetBalance() {
		
		// testing for the initial balance as zero.
		// while constructing the VemdingMachine object we created 4 empty slots an set initial balance to zero
		double balance = 0;
		assertTrue(balance == vm.getBalance());
		
		// testing getBalance after inserting money
		// as the method insertMoney() is tested previously, this is a valid test case based on 
		// using tested insertMoney() method
		vm.insertMoney(20.00);
		// check to see that the balance is not zero even after inserting money
		assertFalse(balance == vm.getBalance());
		assertTrue(20.00 == vm.getBalance());
		
		// test to see that the valid balance is there afer inserting that amount of money
		assertEquals(20.00,vm.getBalance());
		
		
		// test to see that the balance gets added after inserting more money
		vm.insertMoney(5.00);
		assertFalse(balance == vm.getBalance());
		assertFalse(20.00 == vm.getBalance());
		assertTrue(20.00+5.00 == vm.getBalance());
		
	}

	@Test
	public void testMakePurchase() {
		// test to see that makePurchase will return false when the slot is empty
		// purchse cannot be done from an empty slot
		assertFalse(vm.makePurchase("A"));
		
		// adding the item to the vending machine to test makePurchase() method
		// As addItem() functionality has been previously test this is a valid test based on addItem
		vm.addItem(vmi1, "A");
		vm.addItem(vmi2, "B");
		vm.addItem(vmi3, "C");
		vm.addItem(vmi4, "D");

		// No purchase can be made when there is no balance, even when the slot is not empty
		assertFalse(vm.makePurchase("A"));
		
		//trying to purchase an item with less amount than actual cost of the item, negative testing
		vm.insertMoney(1.00);
		assertFalse(vm.makePurchase("A"));
		
		// test case to see that a purchase can be made with valid balance, positive testing
		vm.insertMoney(3.30);
		assertTrue(vm.makePurchase("A"));

		// test to see that the purchase cannot be done by less balance
		assertFalse(vm.makePurchase("A"));
		// test to see that an item can be purchased when the there is 
		//sufficiant balance even after buying an item
		assertTrue(vm.makePurchase("B"));
		
		assertFalse(vm.makePurchase("C"));
		assertTrue(vm.makePurchase("D"));
		
	}

	@Test
	public void testReturnChange() {
		vm.addItem(vmi1, "A");
		vm.addItem(vmi2, "B");
		vm.addItem(vmi3, "C");
		vm.addItem(vmi4, "D");
	
		// test case to verify that there is no amount returned when there is no balance
		assertTrue(0.0 == vm.returnChange());
		assertFalse(vm.makePurchase("D"));	
		
		// test case to test that all the insert money is returned if no purchase is made
		vm.insertMoney(3.30);
		assertTrue(3.30 == vm.returnChange());
		
		// test case to see that only the exact amount is returned after making purchase of an item
		// no less amount or more amount than owed
		vm.insertMoney(3.30);
		vm.makePurchase("D");
		assertTrue(2.30 == vm.returnChange());
		// Checking the balance after returning the amount
		assertTrue(0 == vm.getBalance());
	}

}
